/* sdcard_spi.c */

#include "sdcard_spi.h"

#ifndef SDC_DEBUG_ENABLE
#define SDC_DEBUG_ENABLE    (0)
#endif /* SDC_DEBUG_ENABLE */

#if SDC_DEBUG_ENABLE
#include <stdio.h>
#endif /* SDC_DEBUG_ENABLE */

#define SDC_CMD_RESPONSE_TRY_LOOPS  (100u)
#define SDC_CMD_TRY_LOOPS           (50U)
#define SDC_INIT_RETRY_LOOPS        (10U)

#define SDC_CMD_0  0u
#define SDC_CMD_1  1u

const SDC_SPIHwDrv_T * s_hwDrv;
static bool s_bIsSDHC = false;

/* Interface layer function. */
static void     SDC_SPI_InitHw(void);
static void     SDC_SPI_PutByte(uint8_t txByte);
static uint8_t  SDC_SPI_GetByte(void);
static void     SDC_SPI_SetCS(bool enable);

/* Internal functions. */
static void     SDC_Uint32ToBytes(uint32_t val32, uint8_t *bytes);
static uint8_t  SDC_SpiCrc7(uint8_t crc, uint8_t input[], uint32_t len);
static uint32_t SDC_CsdCapacity(uint8_t *csd);
static bool     SDC_SendSpiCmd(uint8_t cmdNum, uint32_t param, uint8_t *resp);
static bool     SDC_InitCardInternal(SDC_Info_T *infoPtr);
static bool     SDC_Probe(void);

/* 注册底层驱动函数 */
bool SDC_Install(const SDC_SPIHwDrv_T *hwDrv)
{
    s_hwDrv = hwDrv;
    return true;
}

/*
 * 通过74个脉冲复位SD卡，然后查看SD卡是否能够识别CMD0，从而判断SD卡是否上线
 */
static bool SDC_Probe(void)
{
    uint32_t i;
    uint32_t timeout;
    uint8_t resp[4];
    bool    bRet;

#if SDC_DEBUG_ENABLE
    printf("- SDC_Probe() Start ...\r\n");
#endif /* SDC_DEBUG_ENABLE */
    /* pull up the cs. */
    SDC_SPI_SetCS(false);

    /* reset the card with at least 74 clks. */
    for (i = 0u; i < 0xf; i++)
    {
        SDC_SPI_PutByte(0xff);
    }

    /* try the cmd0 to check if the card is ready. */
#if SDC_DEBUG_ENABLE
    printf("CMD0 : check if the card is idle ...\r\n");
#endif /* SDC_DEBUG_ENABLE */    

    timeout = SDC_CMD_TRY_LOOPS;
    bRet = false;
    while (timeout)
    {

        SDC_SendSpiCmd(SDC_CMD_0, 0u, resp);
        if (*resp == 0x01)
        {
#if SDC_DEBUG_ENABLE
            printf("DONE.\r\n");
#endif
            bRet = true;
            break;
        }
        timeout--;
#if SDC_DEBUG_ENABLE
        if (timeout == 0u)
        {
            printf("FAIL.\r\n");
        }
#endif /* SDC_DEBUG_ENABLE */
    }
    
#if SDC_DEBUG_ENABLE
    printf("- SDC_Probe() Done.\r\n");
#endif /* SDC_DEBUG_ENABLE */

    return bRet;
}

bool SDC_InitCard(SDC_Info_T *info)
{
    bool bRet = false;

    /* initialize hardware. */
    SDC_SPI_InitHw();

    /* reset the bus and detect the card. */
    bRet = SDC_Probe();
    if (!bRet)
    {
        return false;
    }

    /* check the card info. */
    bRet = SDC_InitCardInternal(info);
    if (!bRet)
    {
        return false;
    }

    return true;
}






/*
 * CMD8          查看支持的特殊电压模式, 若无响应,则是v1 Normal卡
 * CMD55 + CMD41 初步查看支持卡的类型
 * CMD58         查看卡支持的电压模式，最终确认卡的类型

 */


static bool SDC_InitCardInternal(SDC_Info_T *info)
{
    uint32_t index;
    bool     bRet;
    uint8_t  resp[4];
    uint8_t  csd[16];
    uint32_t timeout;

#if SDC_DEBUG_ENABLE
    printf("- SDC_InitCardInternal() Start ...\r\n");
#endif /* SDC_DEBUG_ENABLE. */


    /*
    * CMD8.
    * Get information from the card. If the particular voltage is supported.
    */
#if SDC_DEBUG_ENABLE
    printf("CMD8: Get voltage info from card ...\r\n");
#endif /* SDC_DEBUG_ENABLE */

    timeout = SDC_CMD_TRY_LOOPS;
    bRet = false;
    while (timeout)
    {
        if ( SDC_SendSpiCmd(8U, 0x000001AAUL, resp) )
        {
            if (0x01U == *resp)
            {
                bRet = true; /* Bingo. */
#if SDC_DEBUG_ENABLE
                printf("DONE.\r\n");
#endif
                break;
            }
        }
        timeout--;
#if SDC_DEBUG_ENABLE
        if (timeout == 0u)
        {
            printf("TIMEOUT. Maybe SDCard v1.0. \r\n");
        }
#endif /* SDC_DEBUG_ENABLE */
    }
    if (!bRet)
    {
        info->CardType = eSDCCardTypeOfSDv1; /* Maybe SD V1.0, but not supported by this lib. */
        SDC_SPI_SetCS(false);
        //return false;
    }
    
if (info->CardType != eSDCCardTypeOfSDv1)
{
    
    /* Get COND information. */
    for (index = 0U; index < 4U; index++)
    {
        resp[index] = SDC_SPI_GetByte();
    }

    SDC_SPI_SetCS(false);
    if ( (resp[2] != 0x01) || (resp[3] != 0xAA) )
    {
#if SDC_DEBUG_ENABLE
        printf(" CMD8 returns wrong data.\r\n");
        printf(" Resp: 0x%X, 0x%X, 0x%X, 0x%X\r\n", resp[0], resp[1], resp[2], resp[3]);
#endif /* SDC_DEBUG_ENABLE */
        return false;
    }
    SDC_SPI_PutByte(0xff); /* dummy send. */











    /* CMD55 + CMD41. Prepare the card. */
    /* Check SDHC first, then SDv2. */
#if SDC_DEBUG_ENABLE
    printf("CMD55+CMD41: Prepare the card ...\r\n");
    printf("Try SDHC card first. CMD41 with 0x40000000.\r\n");
#endif /* SDC_DEBUG_ENABLE */

    timeout = SDC_CMD_TRY_LOOPS;
    bRet = false;
    while (timeout)
    {
        if (SDC_SendSpiCmd(55U, 0U, resp) )
        {
            if (0x01U == *resp)
            {
                bRet = true;
            }
        }
        if ( SDC_SendSpiCmd(41U, 0x40000000U, resp) )
        {
            if (bRet && (0x0U == *resp) ) /* Right response. */ /* 0x01 means busy */
            {
                bRet = true;  /* Bingo. */
                info->CardType = eSDCCardTypeOfSDHC;
#if SDC_DEBUG_ENABLE
                printf("DONE. eSDCCardTypeOfSDHC.\r\n");
#endif /* SDC_DEBUG_ENABLE */
                break;
            }
            else
            {
                bRet = false;
            }
        }
        timeout--;
#if SDC_DEBUG_ENABLE
        printf("TIMEOUT. eSDCCardTypeOfSDHC.\r\n");
#endif /* SDC_DEBUG_ENABLE */
    }

    /* the code escape when timeout. */
    if (!bRet)
    {
        /* try SDv2. */
#if SDC_DEBUG_ENABLE
    printf("Try SDv2 card. CMD41 with 0x0.\r\n");
#endif /* SDC_DEBUG_ENABLE */
        timeout = SDC_CMD_TRY_LOOPS;
        while (timeout)
        {
            if (SDC_SendSpiCmd(55U, 0U, resp) )
            {
                if (0x01 == *resp)
                {
                    bRet = true;
                }
            }
            if ( SDC_SendSpiCmd(41U, 0U, resp) )
            {
                if ( bRet && (0x0U == *resp)) /* Right . response*/ /* 0x01 means busy */
                {
                    bRet = true; /* Bingo. */
                    info->CardType = eSDCCardTypeOfSDv2;
#if SDC_DEBUG_ENABLE
                printf("DONE. eSDCCardTypeOfSDv2.\r\n");
#endif /* SDC_DEBUG_ENABLE */
                    break;
                }
                else
                {
                    bRet = false;
                }
            }
            timeout--;
#if SDC_DEBUG_ENABLE
            if (timeout == 0)
            {
                printf("TIMEOUT. eSDCCardTypeOfSDv2.\r\n");
            }
#endif /* SDC_DEBUG_ENABLE */
        }
    }

    SDC_SPI_SetCS(false);
    if (!bRet)
    {
#if SDC_DEBUG_ENABLE
        printf("FAIL.\r\n");
#endif /* SDC_DEBUG_ENABLE */
        return false;
    }
    SDC_SPI_PutByte(0xff); /* dummy send. */












    /*
    * CMD58.
    * Check the OCR, get support voltage range and card type.
    */
#if SDC_DEBUG_ENABLE
    printf("CMD58: Check the card type ...\r\n");
#endif /* SDC_DEBUG_ENABLE */

    timeout = SDC_CMD_TRY_LOOPS;
    bRet = false;
    while (timeout)
    {
        if ( SDC_SendSpiCmd(58U, 0x000001AAUL, resp) )
        {
            if (0x0 == *resp) /* Right response. */
            {
                bRet = true; /* Bingo */
#if SDC_DEBUG_ENABLE
                printf("CMD58 accepted.\r\n");
#endif /* SDC_DEBUG_ENABLE */
                break;
            }
        }
        timeout--;
#if SDC_DEBUG_ENABLE
        if (timeout)
        {
            printf("FAIL. TIMEOUT.\r\n");
        }
#endif /* SDC_DEBUG_ENABLE */
    }
    if (!bRet)
    {
        SDC_SPI_SetCS(false);
        return false;
    }
    /* 继续读取 OCR 信息. */
    for (index = 0U; index < 4U; index++)
    {
        resp[index] = SDC_SPI_GetByte();
    }
    if (resp[0] & 0x40U) /* 30 bit in OCR - CCS, Card Capicity Statue */
    {
        info->CardType = eSDCCardTypeOfSDHC;
        s_bIsSDHC = true;
        //param = 0x40000000UL;/* Parameter for ACMD41. */
    }
    else
    {
        info->CardType = eSDCCardTypeOfSDv2;
        s_bIsSDHC = false;
        //param = 0UL;/* Parameter for ACMD41. */
    }
    SDC_SPI_SetCS(false);
#if SDC_DEBUG_ENABLE
    printf("DONE.\r\n");
#endif /* SDC_DEBUG_ENABLE */
    SDC_SPI_PutByte(0xFF);






    /*
     * CMD9
     * Get CSD and calc card size.
     */
#if SDC_DEBUG_ENABLE
    printf("CMD9: Get the card size and version ...");
#endif /* SDC_DEBUG_ENABLE */

    timeout = SDC_CMD_TRY_LOOPS;
    bRet = false;
    while (timeout)
    {
        if ( SDC_SendSpiCmd(9U, 0UL, resp) )
        {
            if (0x0U == *resp) /* Right response. */
            {
                bRet = true;
                break;
            }
        }
        timeout--;

#if SDC_DEBUG_ENABLE
        if (timeout == 0u)
        {
            printf("FAIL. TIMEOUT.\r\n");
        }
#endif /* SDC_DEBUG_ENABLE */

    }

    if (!bRet)
    {
        SDC_SPI_SetCS(false);
        return false;
    }

    /* 继续读取参数信息 */

#if SDC_DEBUG_ENABLE
    printf("Wait for the start token.\r\n");
#endif /* SDC_DEBUG_ENABLE */
    timeout = SDC_CMD_RESPONSE_TRY_LOOPS;
    bRet = false;
    while (timeout)
    {
        if (0xFEU == SDC_SPI_GetByte() ) /* Wait for the start token. */
        {
            bRet = true; /* Bingo. */
#if SDC_DEBUG_ENABLE
            printf("DONE.\r\n");
#endif /* SDC_DEBUG_ENABLE */
            break;
        }
        timeout--;
#if SDC_DEBUG_ENABLE
        if (timeout ==  0u)
        {
            printf("FAIL. TIMEOUT.\r\n");
        }
#endif /* SDC_DEBUG_ENABLE */
    }
    if (!bRet)
    {
        SDC_SPI_SetCS(false);
        return false;
    }

    /* 开始读取CSD信息 */

    /* Read CSD. */
    for (index = 0U; index < 16U; index++)
    {
        csd[index] = SDC_SPI_GetByte();
    }
    info->CardBlkCnt = SDC_CsdCapacity(csd); /* 以512字节的块为单位 */

    SDC_SPI_SetCS(false);
    SDC_SPI_PutByte(0xFFU);

#if SDC_DEBUG_ENABLE
    printf("DONE.\r\n");
#endif /* SDC_DEBUG_ENABLE */
    
} /* eSDCCardTypeOfSDv1 */
    
#if SDC_DEBUG_ENABLE
    printf("DONE.\r\n");
    printf("SDC_InitCardInternal() Done.\r\n");
#endif /* SDC_DEBUG_ENABLE */

    return true;
}


bool SDC_WriteBlock(uint32_t blkIdx, uint8_t *txPtr)
{
    uint32_t index, retry;
    bool bRet = false;
    uint8_t r1;

    SDC_SPI_SetCS(false);
    
    /* 在写数之前多发几个空的时钟(片选拉高), 驱动SD卡内部电路准备就绪
     * 若是没有这个步骤,整个SD卡的第一次写块总是会失败,但是后续写入操作就正常
     */
    for (index = 0u; index < 100u; index++)
    {
        SDC_SPI_PutByte(0xFF);
    }
    
    SDC_SPI_SetCS(true);
    
    if (!s_bIsSDHC) /* 以字节寻址的Normal卡,将块地址转换成字节地址,之后作为参数传入SD卡控制器 */
    {
        blkIdx <<= 9u;
    }
#if SDC_DEBUG_ENABLE
    printf("\r\nSDC_WriteBlock(%d, ...) Start ...\r\n", blkIdx);
    printf("Send CMD24 ...\r\n");
#endif /* SDC_DEBUG_ENABLE */
    /*
    * CMD24. SDC_CMD_24_WR_BLOCK
    */
    SDC_SPI_SetCS(true);
    retry = 0U;
    while (1)
    {
        if (SDC_SendSpiCmd(24U, blkIdx, &r1) )
        {
            if (0x0U == r1)
            {
                bRet = true; /* Bingo. */
                break;
            }
        }
        retry++;
        if (retry >= SDC_CMD_TRY_LOOPS)
        {
            bRet = false;
            break;
        }
    }
    if (!bRet)
    {
        SDC_SPI_SetCS(false);
#if SDC_DEBUG_ENABLE
        printf("CMD24 Failed ...\r\n");
#endif /* SDC_DEBUG_ENABLE */
        return false;
    }
#if SDC_DEBUG_ENABLE
    printf("CMD24 OK ...\r\n");
#endif /* SDC_DEBUG_ENABLE */

    /* Insert some clocks. */
    for (index = 0u; index < 100u; index++)
    {
        SDC_SPI_PutByte(0xFF);
    }

    /* Write the start token. */
    SDC_SPI_PutByte(0xFE);
#if SDC_DEBUG_ENABLE
    printf("Writing bytes start ...\r\n");
#endif /* SDC_DEBUG_ENABLE */
    /* Write data. */
    for (index = 0U; index < SDC_BLOCK_SIZE_BYTE; index++)
    {
        SDC_SPI_PutByte(txPtr[index]);
    }

    /* Write 2 byte CRC. */
    SDC_SPI_PutByte(0xFF);
    SDC_SPI_PutByte(0xFF);
#if SDC_DEBUG_ENABLE
    printf("Writing bytes done ...\r\n");
#endif /* SDC_DEBUG_ENABLE */
    /* Wait for the receive response. */
    bRet = false;
    retry = 0U;
    while (1)
    {
        r1 = SDC_SPI_GetByte();
        if (0x05 == (r1 & 0x1F))
        {
            bRet = true;
#if SDC_DEBUG_ENABLE
            printf("Writing data accepted.\r\n");
#endif /* SDC_DEBUG_ENABLE */
            break;
        }
        retry++;
        if (retry >= SDC_CMD_TRY_LOOPS)
        {
            bRet = false;
#if SDC_DEBUG_ENABLE
            printf("Writing data not accpeted.\r\n");
#endif /* SDC_DEBUG_ENABLE */
            break;
        }
    }
    if (!bRet)
    {
        SDC_SPI_SetCS(false);
        return false;
    }

    /* Wait the data to be writen to flash from buffer. */
    while (1)
    {
        r1 = SDC_SPI_GetByte();
        if (0xFF == r1) /* 0xFF = Done, 0x0 = Busy. */
        {
            break;
        }
    }
    SDC_SPI_SetCS(false);
    SDC_SPI_PutByte(0xFF);

    return true;
}

bool SDC_ReadBlock(uint32_t blkIdx, uint8_t *rxPtr)
{
    uint32_t index, retry;
    bool bRet = false;
    uint8_t r1;

    if (!s_bIsSDHC)
    {
        blkIdx <<= 9;
    }
    SDC_SPI_SetCS(true);
    retry = 0U;
    while (1)
    {
        if (SDC_SendSpiCmd(17U, blkIdx, &r1) )
        {
            if (0x0 == r1)
            {
                bRet = true; /* Bingo */
                break;
            }
        }
        retry++;
        if (retry >= SDC_CMD_TRY_LOOPS)
        {
            bRet = false;
            break;
        }
    }
    if (!bRet)
    {
        SDC_SPI_SetCS(false);
        return false;
    }

    /* Wait for the token. */
    while (0xFE != SDC_SPI_GetByte() ) {}

    /* Read data from card. */
    for (index = 0U; index < SDC_BLOCK_SIZE_BYTE; index++)
    {
        rxPtr[index] = SDC_SPI_GetByte();

    }

    /* Read two byte CRC. */
    SDC_SPI_GetByte();
    SDC_SPI_GetByte();

    SDC_SPI_SetCS(false);
    SDC_SPI_PutByte(0xFFU);

    return true;
}

uint32_t SDC_WriteBlocks(uint32_t blkIdx, uint32_t blkcnt, uint8_t *txPtr)
{
    uint32_t index, retry;
    bool bRet = false;
    uint8_t r1;

    if (!s_bIsSDHC)
    {
        blkIdx <<= 9;
    }

    /* ACMD23. */
    SDC_SPI_SetCS(true);
    SDC_SendSpiCmd(23U, blkcnt, &r1);
#if SDC_DEBUG_ENABLE
    printf("Sending CMD25.\r\n");
#endif /* SDC_DEBUG_ENABLE */
    /* CMD25. */
    retry = 0U;
    while (1)
    {
        if (SDC_SendSpiCmd(25U, blkIdx, &r1) )
        {
            if (0x0 == r1)
            {
                bRet = true; /* Bingo */
                break;
            }
        }
        retry++;
        if (retry >= SDC_CMD_TRY_LOOPS)
        {
            bRet = false;
            break;
        }
    }
    if (!bRet)
    {
        SDC_SPI_SetCS(false);
#if SDC_DEBUG_ENABLE
        printf("CMD25 Failed\r\n");
#endif /* SDC_DEBUG_ENABLE */
        return blkcnt;
    }

#if SDC_DEBUG_ENABLE
    printf("CMD25 OK\r\n");
#endif /* SDC_DEBUG_ENABLE */

    /* Write two dummy bytes. */
    SDC_SPI_PutByte(0xFFU);
    SDC_SPI_PutByte(0xFFU);

    while (blkcnt)
    {
#if SDC_DEBUG_ENABLE
        printf("Still %d count\r\n", blkcnt);
#endif /* SDC_DEBUG_ENABLE */
        /* Write start token. */
        SDC_SPI_PutByte(0xFC);
        /* Write data. */
        for (index = 0U; index < SDC_BLOCK_SIZE_BYTE; index++ )
        {
            SDC_SPI_PutByte(txPtr[index]);
        }
        /* Write two dummy CRC. */
        SDC_SPI_PutByte(0xFFU);
        SDC_SPI_PutByte(0xFFU);

        /* Wait the response. */
        bRet = false;
        retry = 0U;
        while (retry <= SDC_CMD_RESPONSE_TRY_LOOPS)
        {
            r1 = SDC_SPI_GetByte();
            if (0x05U == (0x1FU & r1) )
            {
                bRet = true; /* Bingo. */
                break;
            } 
            retry++;
        }
#if SDC_DEBUG_ENABLE
        printf("Get response: OK\r\n");
#endif /* SDC_DEBUG_ENABLE */
        if (!bRet)
        {
#if SDC_DEBUG_ENABLE
            printf("Get response: Failed\r\n");
#endif /* SDC_DEBUG_ENABLE */
            break;
        }
        /* Wait all the data programm finished  */
        retry = 0U;
        bRet = false;
        while (retry <= SDC_CMD_RESPONSE_TRY_LOOPS)
        {
            r1 = SDC_SPI_GetByte();
            if (0xFF == r1)
            {
                bRet = true; /* Bingo. */
                break;
            }
            retry++;
        }
#if SDC_DEBUG_ENABLE
        printf("Wait Flash: OK\r\n");
#endif /* SDC_DEBUG_ENABLE */
        if (!bRet)
        {
#if SDC_DEBUG_ENABLE
            printf("Wait Flash: Failed\r\n");
#endif /* SDC_DEBUG_ENABLE */
            break;
        }
        blkcnt--;
    }
    if (!bRet)
    {
        SDC_SPI_SetCS(false);
        return blkcnt;
    }
    /* Send end of transmit token: 0xFD */
    SDC_SPI_PutByte(0xFD);
#if SDC_DEBUG_ENABLE
    printf("Data end\r\n");
#endif /* SDC_DEBUG_ENABLE */
    /* Wait for finishing programming and then card is idle. */
    retry = 0U;
    bRet = false;
    while (retry <= SDC_CMD_RESPONSE_TRY_LOOPS)
    {
        r1 = SDC_SPI_GetByte();
        if (0xFFU == r1)
        {
            bRet = true;
            break; /* Bingo. */
        }
        retry++;
    }

    SDC_SPI_SetCS(false);
    SDC_SPI_PutByte(0xFFU);
    SDC_SPI_PutByte(0xFFU);
    SDC_SPI_PutByte(0xFFU);

    return blkcnt;
}

uint32_t SDC_ReadBlocks(uint32_t blkIdx, uint32_t blkcnt, uint8_t *rxPtr)
{
    uint32_t index, retry;
    bool bRet = false;
    uint8_t r1;

#if SDC_DEBUG_ENABLE
    printf("SDC_ReadBlocks(%d, %d, ...) start.\r\n", blkIdx, blkcnt);
#endif /* SDC_DEBUG_ENABLE */
    if (!s_bIsSDHC)
    {
        blkIdx <<= 9;
    }
#if SDC_DEBUG_ENABLE
    printf("CMD18 Start.\r\n");
#endif /* SDC_DEBUG_ENABLE */
    /* CMD18. */
    SDC_SPI_SetCS(true);
    retry = 0U;
    while (1)
    {
        if (SDC_SendSpiCmd(18U, blkIdx, &r1) )
        {
            if (0x0 == r1)
            {
                bRet = true; /* Bingo */
                break;
            }
        }
        retry++;
        if (retry >= SDC_CMD_TRY_LOOPS)
        {
            bRet = false;
            break;
        }
    }
    if (!bRet)
    {
        SDC_SPI_SetCS(false);
#if SDC_DEBUG_ENABLE
        printf("CMD18 Failed.\r\n");
#endif /* SDC_DEBUG_ENABLE */
        return blkcnt;
    }
#if SDC_DEBUG_ENABLE
    printf("CMD18 OK.\r\n");
#endif /* SDC_DEBUG_ENABLE */
    /* Wait for the token. */
    while (blkcnt)
    {
#if SDC_DEBUG_ENABLE
        printf("Still %d block.\r\n", blkcnt);
#endif /* SDC_DEBUG_ENABLE */
        /* Wait for the token. */
        while (0xFE != SDC_SPI_GetByte() ) {}
        /* Read block data. */
        for (index = 0U; index < SDC_BLOCK_SIZE_BYTE; index++)
        {
            rxPtr[index] = SDC_SPI_GetByte();
        }
        /* Read two byte CRC. */
        SDC_SPI_GetByte();
        SDC_SPI_GetByte();
        rxPtr += SDC_BLOCK_SIZE_BYTE;
        blkcnt--;
    }
#if SDC_DEBUG_ENABLE
    printf("Data end.\r\n");
#endif /* SDC_DEBUG_ENABLE */

    /* CMD12. STOP_TRANSMISSION */
    SDC_SendSpiCmd(12U, 0U, &r1);
#if SDC_DEBUG_ENABLE
    printf("CMD12 Done.\r\n");
#endif /* SDC_DEBUG_ENABLE */

    SDC_SPI_SetCS(false);
    SDC_SPI_PutByte(0xFFU);
    SDC_SPI_PutByte(0xFFU);
    SDC_SPI_PutByte(0xFFU);

#if SDC_DEBUG_ENABLE
    printf("SDC_ReadBlocks() end.\r\n");
#endif /* SDC_DEBUG_ENABLE */

    return blkcnt;
}

/****************************************************************************/
/* Interface layer functions of callback. */
/****************************************************************************/
static void SDC_SPI_InitHw(void)
{
    (*(s_hwDrv->SPI_InitHwFunc))();
}

static void SDC_SPI_PutByte(uint8_t txByte)
{
    (*(s_hwDrv->SPI_SwapByteFunc))(txByte);
}

static uint8_t SDC_SPI_GetByte(void)
{
    return (*(s_hwDrv->SPI_SwapByteFunc))(0xFF);
}

static void SDC_SPI_SetCS(bool enable)
{
    (*(s_hwDrv->SPI_AssertCsFunc))(enable);
}
/****************************************************************************/
/****************************************************************************/


/****************************************************************************/
/* Internal functions. */
/****************************************************************************/
static void SDC_Uint32ToBytes(uint32_t val32, uint8_t *bytes)
{
#if SDC_HOST_MEMORY_IS_BIG_ENDIAN /* Big Endian. */
        bytes[3] = (uint8_t)(val32 >> 24);
        bytes[2] = (uint8_t)(val32 >> 16);
        bytes[1] = (uint8_t)(val32 >> 8);
        bytes[0] = (uint8_t)(val32);
#else /* Little Endian. */
        bytes[0] = (uint8_t)(val32 >> 24);
        bytes[1] = (uint8_t)(val32 >> 16);
        bytes[2] = (uint8_t)(val32 >> 8);
        bytes[3] = (uint8_t)(val32);
#endif
}

/* 计算CRC */
static uint8_t SDC_SpiCrc7(uint8_t crc, uint8_t input[], uint32_t len)
{
    /* pre-calculated table for CRC7 polynomial 0x89 */
    static const uint8_t crc_table[] = 
    {
        0x00, 0x09, 0x12, 0x1B, 0x24, 0x2D, 0x36, 0x3F,
        0x48, 0x41, 0x5A, 0x53, 0x6C, 0x65, 0x7E, 0x77
    };

    uint8_t index;

    while (len) {
        index = ((crc>>3) & 0x0f) ^ ((*input)>>4);
        crc = (crc<<4) ^ crc_table[index];

        index = ((crc>>3) & 0x0f) ^ ((*input)&0x0f);
        crc = (crc<<4) ^ crc_table[index];

        input++;
        len--;
    }

    return (crc & 0x7f);
}

static uint32_t SDC_CsdCapacity(uint8_t *csd)
{
    uint32_t capacity, c_size, c_size_mult, read_bl_len;

    /* check CSD version */
    if (0 == (csd[0] & 0xC0))
    {
        read_bl_len = csd[5] & 0x0F;
        c_size = csd[6] & 0x03;
        c_size = (c_size << 8) | csd[7];
        c_size = (c_size << 2) | (csd[8] >> 6);
        c_size_mult = ((csd[9] & 0x03) << 1) | (csd[10] >> 7);
        capacity = (c_size + 1) * (1 << (c_size_mult + 2)) * (1 << (read_bl_len - 9)); /* 普通卡,以字节为单位 */
    }
    else
    {
        c_size = csd[7] & 0x3F;
        c_size = (c_size << 8) | csd[8];
        c_size = (c_size << 8) | csd[9];
        capacity = (c_size + 1) << 10; /* 高容量卡,以512字节的块为单位 */
    }

    return capacity;
}

static bool SDC_SendSpiCmd(uint8_t cmdNum, uint32_t param, uint8_t *resp)
{
    uint8_t buff[6];
    uint8_t index;
    bool bRet;

#if SDC_DEBUG_ENABLE
    printf("## Sending CMD %d, param: 0X%X ...  ", cmdNum, param);
#endif /* SDC_DEBUG_ENABLE */
    SDC_SPI_SetCS(false);
    SDC_SPI_PutByte(0xFF);
    SDC_SPI_SetCS(true);

    buff[0] = cmdNum | 0x40;
    SDC_Uint32ToBytes(param, &buff[1]);
    buff[5] = (SDC_SpiCrc7(0, buff, 5) << 1) | 0x01;

    for (index = 0; index < 6; index++)
    {
        SDC_SPI_PutByte(buff[index]);
    }
    //SDC_SPI_PutCharInternal(instance, 0xFF); /* 临时添加 */
    index = 0;
    while (1)
    {
        *resp = SDC_SPI_GetByte();
        if (0xFF != *resp)
        {
            bRet = true;
#if SDC_DEBUG_ENABLE
            printf("DONE. resp: 0x%X\r\n", *resp);
#endif /* SDC_DEBUG_ENABLE */
            break;
        }
        index++;
        if (index >= SDC_CMD_RESPONSE_TRY_LOOPS)
        {
            bRet = false;
#if SDC_DEBUG_ENABLE
            printf("TIMEOUT.\r\n");
#endif /* SDC_DEBUG_ENABLE. */
            break;
        }
    }

    return bRet;
}
/****************************************************************************/
/****************************************************************************/
